package com.sky.sd.system.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sky.sd.common.exception.ServiceException;
import com.sky.sd.system.bean.dto.LoginUser;
import com.sky.sd.system.bean.dto.MenuNode;
import com.sky.sd.system.bean.po.Menu;
import com.sky.sd.system.bean.po.RoleMenu;
import com.sky.sd.system.bean.vo.DeleteVo;
import com.sky.sd.system.bean.vo.DeptUpdateVo;
import com.sky.sd.system.bean.vo.MenuAddVo;
import com.sky.sd.system.bean.vo.MenuUpdateVo;
import com.sky.sd.system.constant.SystemConstant;
import com.sky.sd.system.dao.MenuMapper;
import com.sky.sd.system.dao.RoleMenuMapper;
import com.sky.sd.system.enums.EnabledType;
import com.sky.sd.system.enums.MenuType;
import com.sky.sd.system.service.IMenuService;
import com.sky.sd.system.utils.LoginUserHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统菜单 服务实现类
 * </p>
 *
 * @author 弓振
 * @since 2020-06-30
 */
@Service
@Slf4j
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IMenuService {

    @Autowired
    private RoleMenuMapper roleMenuMapper;

    @Override
    public List<MenuNode> listLoginUserOfMenu() {
        LoginUser loginUser = LoginUserHolder.getLoginUser(SecurityContextHolder.getContext().getAuthentication());
        log.info("  ==> 根据用户{}获取菜单",loginUser.getUsername());
        Set<String> roleIds = loginUser.getRoles().parallelStream().map(role -> role.getId()).collect(Collectors.toSet());
        List<Menu> menus = baseMapper.listMenusByRoleIds(new HashSet<>(roleIds), MenuType.MENU);
        return initTree(menus);
    }

    @Override
    public List<MenuNode> lisMenu() {
        List<Menu> menus = baseMapper.selectList(Wrappers.<Menu>lambdaQuery().eq(Menu::getType, MenuType.MENU));
        return initTree(menus);
    }

    @Override
    public int checkParentId(String parentId,String id) {
        return assertChildNode(parentId, id);
    }

    @Override
    public void add(MenuAddVo vo) {
        Menu menu = new Menu();
        BeanUtils.copyProperties(vo, menu);
        if(StringUtils.isBlank(vo.getParentId())) {
            // 没有传父id，默认为0
            menu.setParentId(SystemConstant.TOP_PARENT_ID);
        }
        // 设置菜单类型
        menu.setType(MenuType.MENU.getValue());
        baseMapper.insert(menu);
    }

    @Override
    public void update(MenuUpdateVo vo) {
        Menu menu = new Menu();
        BeanUtils.copyProperties(vo, menu);
        if(StringUtils.isBlank(vo.getParentId())) {
            // 没有传父id，默认为0
            menu.setParentId(SystemConstant.TOP_PARENT_ID);
        }

        // 判断父节点是否改变，改变->判断当前选中节点是否为该节点下子节点，如果是，则返回失败
        Menu curMenu = baseMapper.selectById(vo.getId());
        if(!curMenu.getParentId().equals(vo.getParentId())) {
            int result = assertChildNode(vo.getParentId(), vo.getId());
            if(result > 0) {
                throw new ServiceException("不能选择该节点下子节点");
            }
            updateChild(menu);
        } else {
            baseMapper.updateById(menu);
        }
    }

    @Override
    public void updateEnabled(MenuUpdateVo vo) {
        Menu menu = new Menu();
        BeanUtils.copyProperties(vo, menu);
        updateChildEnabled(menu);
    }

    @Override
    public void delete(DeleteVo vo) {
        int roleCnt = assertChildMenuOfRole(vo.getIds().iterator().next());
        if(roleCnt > 0) {
            throw new ServiceException("菜单已被关联不能删除");
        }
        baseMapper.deleteBatchIds(vo.getIds());
    }

    private List<MenuNode> initTree(List<Menu> menus) {
        // 父类向子类转换
        List<MenuNode> menuNodes = menus.parallelStream().map(menu -> {
            MenuNode menuTree = new MenuNode();
            BeanUtils.copyProperties(menu, menuTree);
            return menuTree;
        }).collect(Collectors.toList());

        // 创建树
        if(CollectionUtils.isNotEmpty(menuNodes)) {
            List<MenuNode> topMenuNodes = menuNodes.parallelStream()
                    .filter(menuNode -> StringUtils.equals(SystemConstant.TOP_PARENT_ID, menuNode.getParentId()))
                    .collect(Collectors.toList());
            topMenuNodes.parallelStream().forEach(menuNode -> {
                createMenuTree(menuNode, menuNodes);
            });
            return topMenuNodes;
        }
        return null;
    }

    private void createMenuTree(MenuNode curMenuNode, List<MenuNode> allMenuNodes) {
        List<MenuNode> nextMenuNodes = allMenuNodes.parallelStream()
                .filter(menuNode -> StringUtils.equals(curMenuNode.getId(),menuNode.getParentId()))
                .sorted().collect(Collectors.toList());
        nextMenuNodes.parallelStream().forEach(menuNode -> {
            createMenuTree(menuNode, allMenuNodes);
        });
        if(CollectionUtils.isNotEmpty(nextMenuNodes)) {
            curMenuNode.setChildren(nextMenuNodes);
        }
    }

    /**
     * 判断parentId是否是id的节点或id的子节点
     * @param parentId
     * @param id
     * @return
     */
    private int assertChildNode(String parentId,String id) {
        if(StringUtils.equals(parentId, id)) {
            return 1;
        }

        List<Menu> childMenus = baseMapper.selectList(Wrappers.<Menu>lambdaQuery().eq(Menu::getParentId,id));
        int result = 0;
        for(int i = 0;i < childMenus.size();i++) {
            result = assertChildNode(parentId, childMenus.get(i).getId());
            if(result > 0) {
                return result;
            }
        }
        return 0;
    }

    /**
     * 更新当前节点及其子节点
     * @param menu
     */
    private void updateChild(Menu menu) {
        baseMapper.updateById(menu);
        List<Menu> childMenus = baseMapper.selectList(Wrappers.<Menu>lambdaQuery().eq(Menu::getParentId,menu.getId()));
        childMenus.parallelStream().forEach(childMenu -> {
            Menu tmpMenu = new Menu();
            tmpMenu.setId(childMenu.getId());
            updateChild(tmpMenu);
        });
    }

    /**
     * 更新当前节点及其字节
     * @param menu
     */
    private void updateChildEnabled(Menu menu) {
        baseMapper.updateById(menu);
        List<Menu> childMenus = baseMapper.selectList(Wrappers.<Menu>lambdaQuery().eq(Menu::getParentId,menu.getId()));
        childMenus.parallelStream().forEach(childMenu -> {
            Menu tmpMenu = new Menu();
            tmpMenu.setId(childMenu.getId());
            tmpMenu.setEnabled(menu.getEnabled());
            updateChildEnabled(tmpMenu);
        });
    }

    /**
     * 判断当前节点及其子节点是否有角色关联
     * @param parentId
     * @param deleteMenuIds
     */
    private int assertChildMenuOfRole(String id) {
        Integer roleCnt = roleMenuMapper.selectCount(Wrappers.<RoleMenu>lambdaQuery().eq(RoleMenu::getMenuId, id));
        if(roleCnt > 0) {
            return roleCnt;
        }

        List<Menu> childrenMenu = baseMapper.selectList(Wrappers.<Menu>lambdaQuery().eq(Menu::getParentId, id));
        int result = 0;
        for (Menu menu : childrenMenu) {
            result = assertChildMenuOfRole(menu.getId());
            if(result > 0) {
                return result;
            }
        }
        return result;
    }
}
